package app

import (
	"chatgpt/helper"
	"chatgpt/helper/code"
	"chatgpt/logq"
	"chatgpt/model/app"
	"chatgpt/services/datasrv"
	"chatgpt/services/datasrv/cache"
	"chatgpt/services/datasrv/sqlhelper"
	"context"
	"github.com/google/uuid"
	"github.com/slclub/go-tips"
	"time"
)

type UserSrv struct {
	Cache UserCache
}

func (self *UserSrv) NewUser() *app.User {
	return &app.User{
		UUID:    uuid.New().String(),
		LoginAt: tips.String(time.Now().Unix()),
	}
}

func (self *UserSrv) CheckAccount(user *app.User) code.CODENO {
	usrrtn := self.GetFirstDB(&sqlhelper.Where{
		Conds:   []*sqlhelper.CondItem{sqlhelper.WhereCond("NickName", user.NickName), sqlhelper.WhereCond("Login", user.Login)},
		Formula: "{1} or {2}",
	})
	if usrrtn == nil {
		return code.SUCCESS
	}
	if usrrtn.NickName == user.NickName {
		return code.NON_NICKNAME_REPEAT
	}
	if usrrtn.Login == user.Login {
		return code.NON_ACCOUNT_REPEAT
	}
	return code.NON_MAIN_FIELD_REPEAT
}

func (self *UserSrv) CraetedDB(user *app.User) code.CODENO {
	res := sqlhelper.ResWhere{MsgCode: code.SUCCESS}
	m := user.ConvToMap()
	delete(m, "DeletedAt")
	delete(m, "UpdatedAt")
	delete(m, "CreatedAt")

	datasrv.AppDB.UserSave(context.Background(), &sqlhelper.ReqWhere{
		Object: m,
	}, &res)
	return res.MsgCode
}

func (self *UserSrv) Create(user *app.User) code.CODENO {
	return self.CraetedDB(user)
}

func (self *UserSrv) Save(user *app.User) code.CODENO {
	res := sqlhelper.ResWhere{MsgCode: code.SUCCESS}
	m := user.ConvToMap()
	delete(m, "DeletedAt")
	delete(m, "UpdatedAt")
	delete(m, "CreatedAt")

	datasrv.AppDB.UserSave(context.Background(), &sqlhelper.ReqWhere{
		Object: m,
		Where: &sqlhelper.Where{
			Conds: []*sqlhelper.CondItem{
				sqlhelper.WhereCond("UUID", user.UUID),
			},
		},
	}, &res)
	return res.MsgCode
}

func (self *UserSrv) GetWithAccountDB(login string) *app.User {
	return self.GetFirstDB(&sqlhelper.Where{
		Conds:   []*sqlhelper.CondItem{sqlhelper.WhereCond("Login", login)},
		Formula: "{1}",
	})
}

func (self *UserSrv) GetWithFieldDB(field, value string) *app.User {
	return self.GetFirstDB(&sqlhelper.Where{
		Conds:   []*sqlhelper.CondItem{sqlhelper.WhereCond(field, value)},
		Formula: "{1}",
	})
}

func (self *UserSrv) Login(usr *app.User) string {
	self.Cache.ClearWith(usr.UUID)
	token := helper.LoginAccount.AccountPassword(usr.UUID + usr.NickName + time.Now().String())
	m := usr.ConvToMap()
	m["Token"] = token
	self.Cache.SetWithUser(m)
	self.Cache.SetToken(token, map[string]any{
		"UUID":    usr.UUID,
		"LoginAt": time.Now().Unix(),
	})
	return token
}

func (self *UserSrv) GetUserWithUUID(uuid string) *app.User {
	usr := self.Cache.GetByUUID(uuid)
	if usr != nil {
		return usr
	}
	return self.GetFirstDB(&sqlhelper.Where{
		Conds:   []*sqlhelper.CondItem{sqlhelper.WhereCond("Login", uuid)},
		Formula: "{1}",
	})
}

func (self *UserSrv) GetFirstDB(where *sqlhelper.Where) *app.User {
	res := sqlhelper.ResWhere{}
	mc := datasrv.AppDB.UserFirst(context.Background(), &sqlhelper.ReqWhere{
		Where: where,
	}, &res)
	if res.MsgCode != code.SUCCESS {
		logq.InfoF("USERSRV.GET code:%v", mc)
		return nil
	}
	data, ok := res.Data.(map[string]any)
	if !ok || tips.String(data["UUID"]) == "" {
		return nil
	}
	usr := app.User{}
	usr.ConvFromMap(data)
	return &usr
}

func (self *UserSrv) GetUserByToken(token string) *app.User {
	uuid := self.Cache.GetUUIDWithToken(token)
	if uuid == "" {
		return nil
	}
	return self.GetUserWithUUID(uuid)
}

// Login Token

type UserCache struct {
}

func (self *UserCache) GetByUUID(uuid string) *app.User {
	mm := self.GetAnyByUUID(uuid)
	if mm == nil {
		return nil
	}
	usr := app.User{}
	usr.ConvFromMap(mm)
	return &usr
}

func (self *UserCache) GetAnyByUUID(uuid string) map[string]any {
	m := cache.HGetAll(self.CacheKey(uuid))
	if m == nil || len(m) == 0 {
		return nil
	}
	mm := helper.ConvMapStringToMapAny(m)
	return mm
}

func (self *UserCache) CacheKey(an string) string {
	return helper.APP_USER_SESSION + an
}

func (self *UserCache) SetWithUser(data any) {
	if usr, ok := data.(*app.User); ok {
		cache.HMSet(self.CacheKey(usr.UUID), usr.ConvToMap(), helper.EXPIRE_USER_SESSION)
	}
	if m, ok := data.(map[string]any); ok {
		if uid, ok := m["UUID"].(string); ok {
			cache.HMSet(self.CacheKey(uid), m, helper.EXPIRE_USER_SESSION)
		}
	}
}

func (self *UserCache) GetUUIDWithToken(token string) string {
	return cache.HGet(helper.APP_USER_TOKENS+(token), "UUID")
}

func (self *UserCache) GetDataToken(token string) map[string]any {
	m := cache.HGetAll(helper.APP_USER_TOKENS + (token))
	if m == nil {
		return nil
	}
	return helper.ConvMapStringToMapAny(m)
}

func (self *UserCache) SetToken(token string, data map[string]any) {
	cache.HMSet(helper.APP_USER_TOKENS+(token), data, helper.EXPIRE_USER_SESSION)
}

func (self *UserCache) ClearWith(uuid string) {
	usr := self.GetAnyByUUID(uuid)
	if usr == nil {
		return
	}
	token, _ := usr["Token"].(string)
	cache.Del(helper.APP_USER_TOKENS + token)
	cache.Del(self.CacheKey(uuid))
}
